﻿/////////////////////////////////////////////////////////////////////////
//
// This module provides sample code used to demonstrate the use
// of the KinectAudioSource for speech recognition in a game setting.
//
// Copyright © Microsoft Corporation.  All rights reserved.  
// This code is licensed under the terms of the 
// Microsoft Kinect for Windows SDK (Beta) from Microsoft Research 
// License Agreement: http://research.microsoft.com/KinectSDK-ToU
//
/////////////////////////////////////////////////////////////////////////
/*
 * IMPORTANT: This sample requires the following components to be installed:
 * 
 * Speech Platform Runtime (v10.2) x86. Even on x64 platforms the x86 needs to be used because the MSR Kinect SDK runtime is x86
 * http://www.microsoft.com/downloads/en/details.aspx?FamilyID=bb0f72cb-b86b-46d1-bf06-665895a313c7
 *
 * Kinect English Language Pack: MSKinectLangPack_enUS.msi 
 * http://go.microsoft.com/fwlink/?LinkId=220942
 * 
 * Speech Platform SDK (v10.2) 
 * http://www.microsoft.com/downloads/en/details.aspx?FamilyID=1b1604d3-4f66-4241-9a21-90a294a5c9a4&displaylang=en
 * */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.Research.Kinect.Audio;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;
using NIP.Speech.Enumerations;
using NIP.Speech.EventArgs;
using System.Collections;
namespace NIP.Speech
{
    public class Recognizer
    {
                
        public event EventHandler<CommandRecognizedEventArgs> CommandRecognized;
        public event EventHandler<EventArgs.CommandRecognized_Dictionary_EventArgs> CommandRecognized_Dictionary; 
    
        private KinectAudioSource kinectSource;
        private SpeechRecognitionEngine sre;
        private const string RecognizerId = "SR_MS_en-US_Kinect_10.0";
        private bool paused = false;
        private bool valid = false;
        private string grammarFileName = "Grammar\\StateGrammar_ByName.xml";
        Grammar grammar;
        public Recognizer()
        {
            RecognizerInfo ri = SpeechRecognitionEngine.InstalledRecognizers().Where(r => r.Id == RecognizerId).FirstOrDefault();
            if (ri == null)
            {
                throw new NullReferenceException();
            }
                
            sre = new SpeechRecognitionEngine(ri.Id);
            if (sre  == null)
            {
                throw new NullReferenceException();
            }
                
            // Build a simple grammar of shapes, colors, and some simple program control

            grammar = new Grammar(grammarFileName,"IdleDialogState");
            sre.LoadGrammar(grammar);
            sre.SpeechRecognized += sre_SpeechRecognized;
            sre.SpeechHypothesized += sre_SpeechHypothesized;
            sre.SpeechRecognitionRejected += new EventHandler<SpeechRecognitionRejectedEventArgs>(sre_SpeechRecognitionRejected);

            var t = new Thread(StartDMO);
            t.Start();

            valid = true;
	    }

        /// <summary>
        /// Activate top level rules for State
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void StateInitiated_Handler(object sender, System.EventArgs args)
        {
            var ruleName = ((Dialog.IDialogState)sender).Type.ToString();
            try
            {

                grammar = new Grammar(grammarFileName, ruleName);
                sre.LoadGrammar(grammar);
                Debug.WriteLine("Activated Rule " + ruleName);
            }
            catch
            {
                throw new Exception("Fail in switching Grammar for State: " + ruleName);
            }
        }
        public void StateCompleted_Handler(object sender, System.EventArgs args)
        {
            var ruleName = ((Dialog.IDialogState)sender).Type.ToString();
            try
            {
                sre.UnloadAllGrammars();
                //grammar.CmdSetRuleState(ruleName,
                //    SpeechRuleState.SGDSInactive);
                //var rule = (SpeechRuleAttributes)grammar.Rules.FindRule(ruleName).Attributes;
                //grammar.Rules.Commit();
                Debug.WriteLine("Deactivated Rule" + ruleName);
            }
            catch
            {
                throw new Exception("Fail in removing Grammar for State: " + ruleName);
            }
        }

        public bool IsValid()
        {
            return valid;
        }

        private void StartDMO()
        {
            kinectSource = new KinectAudioSource();
            kinectSource.SystemMode = SystemMode.OptibeamArrayOnly;
            kinectSource.FeatureMode = true;
            kinectSource.MicArrayMode = MicArrayMode.MicArrayExternBeam;
            kinectSource.MicArrayBeamAngle = 0.0;
            kinectSource.GainBounder = true;

            kinectSource.AutomaticGainControl = false;
            
            kinectSource.AcousticEchoSuppression = 0;
            kinectSource.MicArrayPreprocess = true;
            kinectSource.NoiseSuppression = true;
            //kinectSource.NoiseFill = true;
            var kinectStream = kinectSource.Start();
            sre.SetInputToAudioStream(kinectStream, new SpeechAudioFormatInfo(
                                                  EncodingFormat.Pcm, 16000, 16, 1,
                                                  32000, 2, null));
            sre.RecognizeAsync(RecognizeMode.Multiple);
        }

        public void Stop()
        {
            if (sre != null)
            {
                sre.RecognizeAsyncCancel();
                sre.RecognizeAsyncStop();
                kinectSource.Dispose();
            }
        }

        void sre_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            Console.WriteLine("\nSpeech Rejected");
        }

        void sre_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            Console.Write("\rSpeech Hypothesized: \t{0}", e.Result.Text);
        }

        void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            Console.Write("\rSpeech Recognized: \t{0} \t{1}", e.Result.Text, e.Result.Confidence);
            
            ArrayList _actionList = new ArrayList();
            var command = new CommandRecognized_Dictionary_EventArgs();
            if (e.Result.Confidence >= 0.5)
            {
                getCommandInfo(e.Result.Semantics, ref command);
                
            }
            else
            {
                command.recognizedText.Add("none", "none");
            }

            if (CommandRecognized != null)
                CommandRecognized_Dictionary(new object(), command);
            //if (command.Action == "None")
            //    return;

            //if (paused) // Only accept restart or reset
            //{
            //    if (command.Action != "Resume")
            //        return;
            //    paused = false;
            //}
            //else
            //{
            //    if (command.Action == "Resume")
            //        return;
            //}

            //if (command.Action == "Stop")
            //    paused = true;

           
        }
        private void getCommandInfo(SemanticValue _sem, ref CommandRecognizedEventArgs command)
        {
            if (_sem == null) // no children
            {
                return;
            }
            else
            {
                SemanticValue oItem;
                for (int i = 0; i < _sem.Count; i++)
                {
                    oItem = _sem.ElementAt(i).Value;
                    String propName = _sem.ElementAt(i).Key;
                    String key = oItem.ElementAt(0).Key;
                    switch (propName)
                    {
                        case "CommandAction":
                            command.Action = key;
                            break;
                        case "ActionDirection":
                            command.Direction = key;
                            break;
                        case "TestCommand":
                            command.Action = key;
                            break;
                        case "CommandObject":
                            command.Object = key;
                            break;
                        case "CommandTarget":
                            command.Target = key;
                            break;
                        default:
                            break;
                    }

                }
            }
        }
        private void getCommandInfo(SemanticValue _sem, ref CommandRecognized_Dictionary_EventArgs command)
        {
            if (_sem == null) // no children
            {
                return;
            }
            else
            {
                SemanticValue oItem;
                for (int i = 0; i < _sem.Count; i++)
                {
                    oItem = _sem.ElementAt(i).Value;
                    String propName = _sem.ElementAt(i).Key.ToLower();
                    String key = oItem.ElementAt(0).Key.ToLower();
                    command.recognizedText.Add(propName, key);
                }
            }
        }
    }
}